home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 November: Tool Chest / Dev.CD Nov 96 TC / Dev.CD Nov 96 TC.toast / Sample Code / Snippets / Memory / Switch Stack / Switch Stack.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-06-05  |  10.2 KB  |  316 lines  |  [TEXT/MPS ]

  1. /*
  2. **    File:        Switch Stack.c
  3. **
  4. **    Contains:    A simple 68K example of a VBL written in C that runs on a
  5. **                private stack. This won't compile for PowerPC, so don't try it.
  6. **                This is a 68K-only thing.
  7. **
  8. **    Written by:    Jim Luther (Based on the VBL code from the Technical Note
  9. **                "TB 35 - MultiFinder Miscellanea".) 
  10. **
  11. **    Copyright:    © 1993-1995 by Apple Computer, Inc., all rights reserved.
  12. **
  13. **    Change History (most recent first):
  14. **
  15. **         <7>     05/30/95    JML        Added check for VM before using HoldMemory and UnholdMemory.
  16. **         <6>     02/18/95    JML        Added HoldMemory and UnholdMemory calls to keep stack in
  17. **                                    physical memory.
  18. **         <5>     01/27/95    JML        Added .r file for resources
  19. **         <4>     01/27/95    JML        Got rid of the THINK C-isms. Now builds with THINK C, MPW C, and Metrowerks C
  20. **         <3>     11/02/93    JML        Added reentrancy comment to VBL
  21. **         <2>     10/13/93    JML        Minor cleanup
  22. **         <1>     10/08/93    JML        First pass
  23. **
  24. */
  25.  
  26. #include <Types.h>
  27. #include <Memory.h>
  28. #include <QuickDraw.h>
  29. #include <Fonts.h>
  30. #include <Windows.h>
  31. #include <Menus.h>
  32. #include <TextEdit.h>
  33. #include <Dialogs.h>
  34. #include <Events.h>
  35. #include <TextUtils.h>
  36. #include <Retrace.h>
  37. #include <LowMem.h>
  38. #include <Gestalt.h>
  39.  
  40. /*----------------------------------------------------------------------------*/
  41.  
  42. /*
  43. **    Define a struct to keep track of what we need to swap stacks.
  44. **
  45. **    WARNING:    Do not change this structure.  The assembly inlines
  46. **                SwitchtoPrivateStack and RestoreStack depend on the
  47. **                exact order of the fields in this record!
  48. */
  49. struct StackRec
  50. {
  51.     Ptr        ourStackBottom;        /* saved stack bottom where we can find it */
  52.     Ptr        ourStackTop;        /* saved stack top where we can find it */
  53.     Ptr        savedA7;            /* place where VBL saves current A7 */
  54.     Ptr        savedStkLowPt;        /* place where VBL saves StkLowPt */
  55.     Ptr        savedHiHeapMark;    /* place where VBL saves HiHeapMark */
  56. };
  57. typedef struct StackRec StackRec;
  58. typedef StackRec *StackRecPtr;
  59.  
  60. /*
  61. **    Define a struct to keep track of what we need in the VBL.  Put theVBLTask
  62. **    into the struct first because its address will be passed to our VBL task
  63. **    in A0.
  64. */
  65. struct VBLRec
  66. {
  67.     VBLTask        theVBLTask;        /* the VBL task itself */
  68.     long        VBLA5;            /* saved CurrentA5 where we can find it */
  69.     StackRec    stackRecord;
  70. };
  71. typedef struct VBLRec VBLRec;
  72. typedef struct VBLRec *VBLRecPtr;
  73.  
  74. /*----------------------------------------------------------------------------*/
  75.  
  76. /*
  77. **    Constants used in sample
  78. */
  79.  
  80. enum
  81. {
  82.     kInterval        =    6,        /* VBL interval */
  83.     rInfoDialog        =    140,    /* DLOG resource ID */
  84.     rStatTextItem    =    1,        /* item number of counter field in dialog */
  85.     
  86.     kStackSize         =     0x4000    /* 16K */
  87. };
  88.  
  89. /*----------------------------------------------------------------------------*/
  90.  
  91. /*
  92. **    Prototypes
  93. */
  94.  
  95. pascal void    DoVBL (VBLRecPtr recPtr);
  96. void        StartVBL(void);
  97. void        main(void);
  98.  
  99. /*----------------------------------------------------------------------------*/
  100.  
  101. /*
  102. **    A global which will be referenced from our VBL Task and the test program
  103. */
  104.  
  105. long    gCounter;        /* Counter incremented each time our VBL gets called */
  106. long    gVMOn = false;    /* true if System 7 VM is turned on */
  107.  
  108. /*----------------------------------------------------------------------------*/
  109.  
  110. /*
  111. **    GetVBLRec returns the address of the VBLRec associated with our VBL task.
  112. **    This works because on entry into the VBL task, A0 points to the theVBLTask
  113. **    field in the VBLRec record, which is the first field in the record and that
  114. **    is the address we return.  Note that this method works whether the VBLRec
  115. **    is allocated globally, in the heap (as long as the record is locked in 
  116. **    memory) or if it is allocated on the stack as is the case in this example.
  117. **    In the latter case this is OK as long as the procedure which installed the
  118. **    task does not exit while the task is running.  This trick allows us to get
  119. **    to the saved A5, but it could also be used to get to anything we wanted to
  120. **    store in the record.
  121. */
  122. extern    VBLRecPtr GetVBLRec(void)
  123.     = 0x2008;    /* MOVE.L    A0,D0 */
  124.  
  125. /*
  126. **    SwitchtoPrivateStack and RestoreStack are assembly language inlines
  127. **    that perform the stack switch using the StackRec passed as a parameter
  128. **    to SwitchtoPrivateStack.
  129. */
  130.  
  131. #pragma parameter SwitchtoPrivateStack(__A0)
  132. extern    void    SwitchtoPrivateStack(StackRec *sr)
  133.     =    {                            /*                                                    ; A0 = the StackRecPtr parameter */
  134.             0x2178, 0x0BAE, 0x0010,    /* MOVE.L    HiHeapMark,StackRec.savedHiHeapMark(A0)    ; save HiHeapMark */
  135.             0x2178, 0x0110, 0x000C,    /* MOVE.L    StkLowPt,StackRec.savedStkLowPt(A0)        ; save StkLowPt */
  136.             0x214f, 0x0008,            /* MOVE.L    SP,StackRec.savedA7(A0)                    ; save current system stack */
  137.             0x42b8, 0x0110,            /* CLR.L    StkLowPt                                ; disable the stack sniffer */
  138.             0x21D0, 0x0BAE,            /* MOVE.L    StackRec.ourStackBottom(A0),HiHeapMark    ; set HiHeapMark to bottom of our stack */
  139.             0x2e68, 0x0004,            /* MOVEA.L    StackRec.ourStackTop(A0),SP                ; switch stacks */
  140.             0x2f08                    /* MOVE.L    A0,-(SP)                                ; save A0 (*sr) on top of private stack */
  141.         };
  142.  
  143. extern    void    RestoreStack(void)
  144.     =    {
  145.             0x205F,                    /* MOVE.L    (SP)+,A0                                ; restore A0 (*sr) from private stack */
  146.             0x2E68, 0x0008,            /* MOVEA.L    StackRec.savedA7(A0),A7                    ; restore system stack */
  147.             0x21E8, 0x0010, 0x0BAE,    /* MOVE.L    StackRec.savedHiHeapMark(A0),HiHeapMark    ; restore HiHeapMark */
  148.             0x21E8, 0x000C, 0x0110    /* MOVE.L    StackRec.savedStkLowPt(A0),StkLowPt        ; restore the sniffer */
  149.         };
  150.  
  151. /*----------------------------------------------------------------------------*/
  152.  
  153. /*
  154. **    DoVBL is called only by StartVBL()
  155. **
  156. **    WARNING:    It MUST be declared "pascal" so the function will remove the
  157. **                parameters itself!
  158. */
  159. pascal    void DoVBL (VBLRecPtr recPtr)
  160. {
  161.     gCounter++;                                    /* Show we can set a global */
  162.     recPtr->theVBLTask.vblCount = kInterval;    /* Set ourselves to run again */
  163. }
  164.  
  165. /*----------------------------------------------------------------------------*/
  166.  
  167. /*
  168. **    This is the actual VBL task code.  It uses GetVBLRec to get our VBL record
  169. **    and properly set up A5.  Having done that, switches to a private stack and
  170. **    then  calls DoVBL to increment a global counter and sets itself to run again.
  171. **    Because of the vagaries of C optimization, it calls a separate routine to
  172. **    actually access global variables.  See "OV 10 - Setting and Restoring A5"
  173. **    for the reasons for this, as well as for a description of SetA5.
  174. **    I can switch to my private stack without checking to make sure I haven't
  175. **    already done so because VBLs don't have to worry about begin reentrant.
  176. */
  177. void StartVBL ()
  178. {
  179.     long        curA5;
  180.     VBLRecPtr    recPtr;
  181.     
  182.     recPtr = GetVBLRec();            /* First get our record */
  183.     curA5 = SetA5(recPtr->VBLA5);    /* Get the saved A5 */
  184.     /* Now we can access globals */
  185.  
  186.     /* Switch to private stack */
  187.     SwitchtoPrivateStack(&recPtr->stackRecord);
  188.     
  189.     /*    Now we're running on our private stack */
  190.     
  191.     /*
  192.     **    Make sure any functions called at this point use pascal calling conventions
  193.     **    so the functions clean up the stack before returning!
  194.     */
  195.     DoVBL(recPtr);                    /* Call another routine to do actual work */
  196.  
  197.     /* Switch back to original stack */
  198.     RestoreStack();
  199.  
  200.     (void) SetA5(curA5);            /* Restore old A5 */
  201. }
  202.  
  203. /*----------------------------------------------------------------------------*/
  204.  
  205. /*
  206. **    Create a dialog just to demonstrate that the global variable
  207. **    is being updated by the VBL Task.  Before installing the VBL, we store
  208. **    our A5 in the actual VBL Task record, using SetCurrentA5 described in
  209. **    OV 10 - Setting and Restoring A5.  We also store the location of our
  210. **    private stack which the VBL code will run under. We'll run the VBL,
  211. **    showing the counter being incremented, until the mouse button is clicked.
  212. **    Then we remove the VBL Task, close the dialog, and remove the mouse down
  213. **    events to prevent the application from being inadvertently switched
  214. **    by MultiFinder.
  215. */
  216. void main (void)
  217. {
  218.     VBLRec            theVBLRec;
  219.     DialogPtr        infoDPtr;
  220.     DialogRecord    infoDStorage;
  221.     Str255            numStr = "\pTest";
  222.     OSErr            theErr;
  223.     Handle            theItemHandle;
  224.     short            theItemType;
  225.     Rect            theRect;
  226.     long            lastCount = 0;
  227.     long            response;
  228.  
  229.     InitGraf(&qd.thePort);
  230.     InitFonts();
  231.     InitWindows();
  232.     InitMenus();
  233.     TEInit();
  234.     InitDialogs(NULL);
  235.     InitCursor();
  236.     MaxApplZone();
  237.     
  238.     /* check for VM */
  239.     if ( Gestalt(gestaltVMAttr, &response) == noErr )
  240.     {
  241.         gVMOn = ((response & (1L << gestaltVMPresent)) != 0);
  242.     }
  243.     
  244.     /* Store the current value of A5 in the VBLA5 field. */
  245.     theVBLRec.VBLA5 = SetCurrentA5 ();
  246.  
  247.     /* Allocate memory for our private stack and store it's location in ourStackBottom */
  248.     theVBLRec.stackRecord.ourStackBottom = NewPtrSys(kStackSize);
  249.     if ( theVBLRec.stackRecord.ourStackBottom != NULL )
  250.     {
  251.         if ( gVMOn )
  252.         {
  253.             /* Make sure the stack is held in physical memory */
  254.             theErr = HoldMemory(theVBLRec.stackRecord.ourStackBottom, kStackSize);
  255.         }
  256.         else
  257.         {
  258.             theErr = noErr;
  259.         }
  260.         if ( theErr == noErr )
  261.         {
  262.             /* Store location of our stack's top in ourStackTop */
  263.             theVBLRec.stackRecord.ourStackTop =
  264.                 (Ptr)((unsigned long)theVBLRec.stackRecord.ourStackBottom + kStackSize);
  265.             
  266.             gCounter = 0;    /* Initialize our global counter */
  267.             
  268.             /* Put up the dialog */
  269.             infoDPtr = GetNewDialog (rInfoDialog, (Ptr) &infoDStorage, (WindowPtr) -1);
  270.             DrawDialog (infoDPtr);
  271.             GetDItem (infoDPtr, rStatTextItem, &theItemType, &theItemHandle, &theRect);
  272.             
  273.             /* Set the address of our routine */
  274.             theVBLRec.theVBLTask.vblAddr = NewVBLProc(StartVBL);
  275.             theVBLRec.theVBLTask.vblCount = kInterval;    /* Frequency of task, in ticks */
  276.             theVBLRec.theVBLTask.qType = vType;            /* qElement is a VBL task */
  277.             theVBLRec.theVBLTask.vblPhase = 0;
  278.             
  279.             /* Now install the VBL task */
  280.             theErr = VInstall((QElemPtr)&theVBLRec.theVBLTask);
  281.             
  282.             /* Display the counter until the mouse button is pushed */
  283.             if ( theErr == noErr )
  284.             {
  285.                 do
  286.                 {
  287.                     if (gCounter != lastCount)
  288.                     {
  289.                         lastCount = gCounter;
  290.                         NumToString(gCounter, numStr);
  291.                         SetIText(theItemHandle, numStr);
  292.                     }
  293.                 } while ( !Button () );
  294.                 theErr = VRemove((QElemPtr)&theVBLRec.theVBLTask); /* Remove it when done */
  295.             }
  296.             
  297.             /* Finish up */
  298.             CloseDialog (infoDPtr);        /* Get rid of our dialog */
  299.             FlushEvents (mDownMask, 0);    /* Flush all mouse down events */
  300.             
  301.             if ( gVMOn )
  302.             {
  303.                 /* Release the hold on our stack */
  304.                 theErr = UnholdMemory(theVBLRec.stackRecord.ourStackBottom, kStackSize);
  305.             }
  306.             else
  307.             {
  308.                 theErr = noErr;
  309.             }
  310.         }
  311.         
  312.         /* Dispose of the private stack */
  313.         DisposePtr(theVBLRec.stackRecord.ourStackBottom);
  314.     }
  315. }
  316.